import dayjs from 'dayjs';
import { nonNull } from '@/lib/tool';
import { type CookieSerializeOptions, serialize } from 'cookie';
import type { TBody, TParams } from '@/types';
import {
  type NextFetchEvent,
  type NextMiddleware,
  type NextRequest,
  NextResponse,
} from 'next/server';
import { BEARER } from '@/lib/constant';

export const getQueryParams = (
  config: TParams | TBody<any>,
  params: Record<string, any> = {},
  addQuestionMark: boolean = false,
) => {
  const { query, data } = config as TParams & TBody<any>;
  const _params = { ...query, ...data, ...params };

  const newParams: Record<string, any> = Object.fromEntries(
    Object.entries(_params).filter(([_, value]) => nonNull(value)),
  );

  const value = new URLSearchParams(newParams).toString();
  return value && addQuestionMark ? `?${value}` : value;
};

export const createConfig = (
  target: TParams | TBody<any> = {},
  source?: TParams | TBody<any>,
): TParams => {
  const token = source?.token ?? target?.token;
  const config = {
    ...source,
    ...target,
  };

  if (!config.baseURL) {
    config.baseURL = '/api';
  }

  if (
    config.method === 'POST' ||
    config.method === 'PUT' ||
    config.method === 'PATCH'
  ) {
    if (!config.body) {
      if (!config.headers) {
        config.headers = {
          'Content-Type': 'application/json',
        };
      }

      const reqData = (config as TBody<any>).data;
      if (reqData) {
        config.body = JSON.stringify(reqData);
      }
    }
  }

  return token
    ? {
        ...config,
        headers: {
          ...config.headers,
          Authorization: token.startsWith(BEARER) ? token : `${BEARER}${token}`,
        },
      }
    : config;
};

export const handleReqMiddleware = (
  response: Response,
  skipParse?: boolean,
) => {
  return handleReqError(response).then((value) =>
    handleReqData(value, skipParse),
  );
};

export const handleReqError = async (response: Response) => {
  const headersLength = Object.keys(response.headers).length;

  if (
    !response.ok &&
    (response.headers.get('content-type') === 'application/json' ||
      headersLength === 0)
  ) {
    const reason = {
      status: 500,
      message: '未知错误',
      ...(await response.json()),
    };

    if (headersLength === 0) {
      throw reason;
    }
    throw {
      ...reason,
      headers: response.headers,
    };
  }
  return response;
};

export const handleReqData = async (
  response: Response,
  skipParse?: boolean,
) => {
  if (skipParse) {
    return response;
  }

  if (
    isClient() &&
    response.headers.get('content-type')?.includes('application/json')
  ) {
    return (await response.json()).data;
  }
  return response;
};

export const serializeCookie = (
  name: string,
  value: unknown,
  options: CookieSerializeOptions = {},
) => {
  const stringValue =
    typeof value === 'object' ? JSON.stringify(value) : String(value);

  if (typeof options.maxAge !== 'undefined' && !options.expires) {
    options.expires = dayjs().set('seconds', options.maxAge).toDate();
  }

  return serialize(name, stringValue, options);
};

export const isClient = () => {
  return typeof window !== 'undefined';
};

export const isServer = () => {
  return typeof window === 'undefined';
};

export const createMiddlewareChain = (
  middlewares: NextMiddleware[],
): NextMiddleware => {
  return middlewares.reduceRight(
    (next, middleware) => (req: NextRequest, event: NextFetchEvent) =>
      middleware(req, event) ?? next(req, event),
    async () => NextResponse.next(),
  );
};
